import { WebModel } from './webmodel.js'

export class Model extends WebModel {
  constructor(model_name, payload = {}) {
    super(model_name, payload)
  }

  get_fields_all(nodes) {
    // call by read form view,
    //  to get all fields with meta
    return Object.keys(nodes)
      .filter(item => !nodes[item].invisible)
      .reduce((acc, fld) => {
        const meta = this.metadata[fld] || {}

        acc[fld] = {
          meta,
          name: fld,
          ...nodes[fld]
        }

        return acc
      }, {})
  }

  _load_metadata_after(fields, metadata) {
    // console.log(this.model, fields, metadata)
    return Object.keys(fields).reduce((acc, fld) => {
      if (fld in metadata) {
        const meta = metadata[fld]

        if (meta.relation && fields[fld].fields) {
          const child_metadata = this.env
            .model(meta.relation)
            ._load_metadata_after(fields[fld].fields, meta.metadata)

          acc[fld] = { ...meta, metadata: child_metadata }
        } else {
          acc[fld] = meta
        }
      } else {
        const metadata_extend = this.metadata_extend

        if (!(fld in metadata_extend)) {
          console.log(this.model, fld, metadata_extend)
          throw 'error, field name, ' + this.model + ' ' + fld
        }

        const meta_ext = metadata_extend[fld]
        const meta = {
          ...meta_ext,
          name: fld,
          readonly: meta_ext.inverse ? false : true
        }
        this._metadata[fld] = meta
        acc[fld] = meta
      }

      return acc
    }, {})
  }

  async load_metadata(kwargs) {
    // const { fields, context, callback } = kwargs
    // 参数 fields: dict. 可递归处理relation字段. 详细格式 参考 fields的定义
    //   参数 fields 中 包含 view 带过来的 额外的  domain 和 selection
    // 参数 callback, 函数. 分多次返回 包含嵌套子模型的 metadata
    // 返回值. 对象类型. 包含每个字段的属性. 同 odoo的 fields_get函数
    // 若为 relation 字段. 则额外增加一个属性 metadata. 携带子模型的 metadata
    // 该函数有负作用. 将执行结果 metadata 在 模型中暂存

    const { fields, ...kw2 } = kwargs

    const res = await this.web2_fields_get(fields, kw2)
    const res2 = this._load_metadata_after(fields, res)
    this.metadata = { ...res2 }

    return res2
  }

  _fields_remove(fields) {
    const fields2 = Object.keys(fields).reduce((acc, fld) => {
      const meta = this.metadata[fld]

      if (meta.odoojs_extend) {
        //
      } else {
        const params = fields[fld]

        if (meta.relation && 'fields' in params) {
          const child_fields = this.env
            .model(meta.relation, { metadata: meta.metadata })
            ._fields_remove(params.fields)
          acc[fld] = { ...params, fields: child_fields }
        } else {
          acc[fld] = params
        }
      }

      return acc
    }, {})

    return fields2
  }

  _load_web_before(kwargs = {}) {
    const { fields } = kwargs
    const fields2 = this._fields_remove(fields)

    return { ...kwargs, fields: fields2 }
  }

  _load_web_after(records, { fields, context }) {
    const records1 = Array.isArray(records) ? records : [records]
    // console.log(records, fields, records1)
    const records2 = records1.map(one => {
      let one2 = { ...one }

      const compute_fns = {}
      Object.keys(fields).forEach(fld => {
        const meta = this.metadata[fld]
        if (fld in one) {
          const child_fields = fields[fld].fields
          if (child_fields) {
            one2[fld] = this.env
              .model(meta.relation, { metadata: meta.metadata })
              ._load_web_after(one[fld], { fields: child_fields, context })
          }
        } else {
          if (meta.compute && this[meta.compute]) {
            compute_fns[meta.compute] = 1
          }
        }
      })

      Object.keys(compute_fns).forEach(fn => {
        const res = this[fn]({ record: one, context })
        one2 = { ...one2, ...res }
      })

      return one2
    })

    return Array.isArray(records) ? records2 : records2[0]
  }

  // load_web_data load_data_one
  // 参数 fields: dict. 可递归处理relation字段. 详细格式 参考 fields的定义
  //   参数 fields 中 仅仅是 做递归查询需要的内容
  // 参数 callback, 函数. 分多次返回 包含嵌套子模型的 recordset
  // 返回值 各字段的值 标准化处理:
  // 空值 false 改为 null
  // 若为 relation 字段. 标准化处理

  async load_web_data(kwargs = {}) {
    // const { domain, fields, offset, limit, order, context, callback } = kwargs
    // 参数:  接口 web_search_read 需要的参数
    // 返回值. {length, records: []}.

    const kwargs2 = this._load_web_before(kwargs)
    const { domain = [], fields: fs, ...kw2 } = kwargs2

    const res = await this.web2_search_read(domain, fs, kw2)

    const { fields, context = {} } = kwargs
    const { length, records } = res

    const records2 = this._load_web_after(records, { fields, context })

    return { length, records: records2 }
  }

  async load_data_one(res_id, kwargs = {}) {
    // const { fields, context, callback } = kwargs
    // 参数 res_id 数据的 id
    // 返回值. dict. web_read_one 的返回值 并做标准化处理
    const kwargs1 = this._load_web_before(kwargs)
    const { fields: fields0, ...kwargs2 } = kwargs1
    const res = await this.web2_read_one(res_id, fields0, kwargs2)

    const { fields, context = {} } = kwargs
    const res2 = this._load_web_after(res, { fields, context })

    return res2
  }

  async load_data(kwargs = {}) {
    const { records } = await this.load_web_data(kwargs)
    return records
  }

  // todo 调整参数格式,
  // 传入参数为编辑前的 标准化的 record(readonly) 及 values
  // 以及 被编辑的 字段名 及字段值 field_name, val
  // 如果是 o2m 子模型的编辑, 参数还包括 父级模型的  record_readonly values
  // 在本函数中,
  // 1: 将 field_name, val 更新到 values
  // 2: 将 record_readonly 及 values 组织为 odoo.onchange 需要的参数 values
  // 3: 将 odoo.onchange 返回结果标准化后, 更新到 values
  // 4: 将 values 返回
  // 5: 上级函数 将record(readonly) 及 values 组织为 record_display 供页面使用

  async call_onchange(ids, values, field_name, kwargs = {}) {
    // call by Editmodel.call_onchange

    // todo. 2023-12-21
    //
    // odoo17. field_name 必须是数组
    // odoo16 以下 field_name 可是是字段名, 也可以是是数组

    return this.web2_onchange(ids, values, field_name, kwargs)
  }

  async call_commit(res_id, vals, kwargs = {}) {
    // call by Editmodel.call_commit
    return this.web2_save(res_id, vals, kwargs)
  }
}

/*

// help 读取数据的流程. 编辑数据的流程
//
// 10. 初始化创建 Model
// 11. 定义 model = 'sale.order'
// 12. 同步创建 Model = rpc.env.model(model)
// 13. 异步获取 Model.load_metadata({fields, callback})
// 20. 读取tree数据
// 21. records = await Model.load_data({callback, domain,fields, ...})
// 21. 选择一条数据 获取 res_id
// 30. 读取 form 数据
// 31. record = await Model.load_data_one(res_id,{callback})
// 40. 编辑数据
// 41. 使用 Editmodel. 参考 editmodel.js
// 42. 编辑中使用 Model.onchange 函数
// 43. 编辑结束使用 Model.create 和 Model.wrtie 函数
// 44. 编辑完成. 使用 Model.load_data_one 重写刷新数据
//
// help fields参数的使用
// 10. 使用场合
// 11. Model.load_metadata 中使用
// 12. Model.load_data 中使用
// 13. Model.load_data_one 中使用
// 20. 作用
// 21. 提供需要的所有字段.
// 22. 可嵌套包括relatiion 字段对应模型的字段
// 30. fields的 Array 格式
// 31. 数组. 每个元素表示一个字段
// 32. 每个字段通常是字符串类型. 若为嵌套的relation字段则为 对象类型
// 33. Many2one 字段. 暂时未实现 嵌套读取数据. 以后可扩展
// 34. Many2many 字段. 若未嵌套, 则仅需要嵌套读取 display_name 即可
// 35. One2many 字段. 需要嵌套读取子模型的数据
// 40. 嵌套字段的格式
// 41. name: 表示字段的名称
// 42. feilds: 数组类型. 子模型的字段. 可继续嵌套定义 relation字段
// 50. fields的 dict 格式
// 51. 每个 (key, val). key 表示一个字段名. val 通常为 null.
// 52. val 若不为 null. 表示为嵌套的relation字段.格式为 { fields }.
//

// help callback参数的使用
// 10. 使用场合
// 11. Model.load_metadata 中使用
// 12. Model.load_data 中使用
// 13. Model.load_data_one 中使用
// 20. 作用
// 21. 参数 feilds 包含嵌套子模型时. 通过 callback 分多次返回结果
// 22. load_metadata 返回 metadata. 子模型读取完成后, 及时返回
// 23. load_data 返回 列表数据. 子模型读取完成后, 及时返回更新后的数据
// 24. load_data_one 返回 对象数据. 子模型读取完成后, 及时返回更新后的数据
//
// help 返回值的格式
// 11. load_data 返回数组类型
// 12. load_data_one 返回 对象类型 是 load_data 返回数组的 其中一条
// 20. 各类字段的返回结果
// 21. char 原值
// 22. date 字符串格式 'YYYY-MM-DD'
// 23. datetime 字符串格式 UTC 时间 'YYYY-MM-DD HH:MM:DD'. 考虑改为本地时间
// 24. float 浮点数
// 25. many2one {id, display_name}
// 26. many2many {id, display_name}. 未读取子模型之前只有 id
// 27. one2many {id, ... }. 未读取子模型之前只有 id

*/
